using M13.Math;
using System.Collections.Generic;
using UnityEngine;

namespace M13.Game.World
{
    public class ChunkMesh
    {
        public Mesh Mesh { get; private set; }
        public List<Vector3> Vercites { get; private set; } = new List<Vector3>();
        public List<Vector2> Uvs { get; private set; } = new List<Vector2>();
        public List<int> Triangles { get; private set; } = new List<int>();

        private int[] _neighbor = new int[4];

        //˳����-x:0  z:1  x:2  -z:3
        private List<byte[]> _neighborChunkHeightMaps;

        private Dictionary<int, int> _turfIdOrderNumberMap = new Dictionary<int, int>();

        private Dictionary<int, List<int>> _trianglesTurfIdMap = new Dictionary<int, List<int>>();

        private int _turfSpecies = 0;

        private byte[] _chunkTurfIdMap;

        private byte[] _chunkHeightMap;

        public ChunkMesh(byte[] chunkHeightMap, List<byte[]> neighborChunkHeightMaps, byte[] chunkTurfIdMap)
        {
            _neighborChunkHeightMaps = neighborChunkHeightMaps;
            _chunkHeightMap = chunkHeightMap;
            _chunkTurfIdMap = chunkTurfIdMap;
            TurfsTypeOnChunk();
            CalculateVercitesCount(chunkHeightMap, _chunkTurfIdMap);
            SetMesh();
            
        }

        public ChunkMesh(byte[] chunkHeightMap, List<byte[]> neighborChunkHeightMaps)
        {
            this._neighborChunkHeightMaps = neighborChunkHeightMaps;
            TurfsTypeOnChunk();
            CalculateVercitesCount(chunkHeightMap, _chunkTurfIdMap);
            SetMesh();
            
        }

        public ChunkMesh(byte[] chunkHeightMap)
        {
            TurfsTypeOnChunk();
            CalculateVercitesCount(chunkHeightMap, _chunkTurfIdMap);
            SetMesh();
            
        }

        Vector3 _turfPos = new Vector3();
        private void CalculateVercitesCount(byte[] chunkHeightMap, byte[] chunkTurfIdMap)
        {
            for(int i = 0; i < chunkHeightMap.Length; i++)
            {
                Vector2Int v2 = M13Mathf.IndexToXY(i);
                int turfIndex = M13Mathf.XZYToIndex(new Vector3Int(v2.x, chunkHeightMap[i], v2.y));
                _turfPos = new Vector3(i >> 4, (chunkHeightMap[i] - 1) * 0.25f, i % 16);
                NeighborTurfIndex(i);
                for(int j = 0; j < 4; j++)
                {
                    if (_neighbor[j] != -1)
                    {
                        //�ڱ߸߶�С
                        if (chunkHeightMap[_neighbor[j]] < chunkHeightMap[i])
                            SideMeshShape(j, chunkHeightMap[_neighbor[j]], chunkHeightMap[i], _turfPos, chunkTurfIdMap[turfIndex]);
                        continue;
                    }

                    //����������Ե
                    if (_neighborChunkHeightMaps == null)
                        SideMeshShape(j, 0, chunkHeightMap[i], _turfPos, chunkTurfIdMap[turfIndex]);
                    else
                    {
                        int neigChunkEdgeTurfIndex = NeighborChunkEdgeIndex(i, j);
                        byte negiChunkEdgeTurfHeight = _neighborChunkHeightMaps[j][neigChunkEdgeTurfIndex];

                        if (negiChunkEdgeTurfHeight < chunkHeightMap[i])
                            SideMeshShape(j,  negiChunkEdgeTurfHeight, chunkHeightMap[i], _turfPos, chunkTurfIdMap[turfIndex]);
                    }
                }
                TopMeshShape(_turfPos, chunkTurfIdMap[turfIndex]);

                
            }
        }

        int _trianglesStartIndex = 0;
        private void SideMeshShape(int sideIndex, float startHeight, float endHeight, Vector3 turfPos, int turfId)
        {
            int num = (int)(endHeight - startHeight);

            if (_trianglesTurfIdMap[turfId] == null)
            {
                List<int> subTriangles = new List<int>();
                _trianglesTurfIdMap[turfId] = subTriangles;
            }

            if (!_turfIdOrderNumberMap.ContainsKey(turfId))
            {
                _turfIdOrderNumberMap.Add(turfId, _turfIdOrderNumberMap.Count);
            }

            for (int i = 0; i < num; i++)
            {
                int uvsHeight = i % 4;
                Vercites.Add(new Vector3(TurfMeshData.TurfVercites[(sideIndex + 1) * 4].x + turfPos.x, TurfMeshData.TurfVercites[(sideIndex + 1) * 4].y + i * 0.25f + startHeight * 0.25f, TurfMeshData.TurfVercites[(sideIndex + 1) * 4].z + turfPos.z));
                Vercites.Add(new Vector3(TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 1].x + turfPos.x, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 1].y + i * 0.25f + startHeight * 0.25f, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 1].z + turfPos.z));
                Vercites.Add(new Vector3(TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 2].x + turfPos.x, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 2].y + i * 0.25f + startHeight * 0.25f, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 2].z + turfPos.z));
                Vercites.Add(new Vector3(TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 3].x + turfPos.x, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 3].y + i * 0.25f + startHeight * 0.25f, TurfMeshData.TurfVercites[(sideIndex + 1) * 4 + 3].z + turfPos.z));
                Uvs.Add(TurfMeshData.TurfSideUvs[0] + new Vector2(0, uvsHeight * 0.25f));
                Uvs.Add(TurfMeshData.TurfSideUvs[1] + new Vector2(0, uvsHeight * 0.25f));
                Uvs.Add(TurfMeshData.TurfSideUvs[2] + new Vector2(0, uvsHeight * 0.25f));
                Uvs.Add(TurfMeshData.TurfSideUvs[3] + new Vector2(0, uvsHeight * 0.25f));

                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 0);
                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 1);
                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 2);
                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 0);
                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 2);
                _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 3);
                _trianglesStartIndex += 4;
            }
            
        }

        private void TopMeshShape(Vector3 turfPos, int turfId)
        {
            if (_trianglesTurfIdMap[turfId] == null)
            {
                List<int> subTriangles = new List<int>();
                _trianglesTurfIdMap[turfId] = subTriangles;
            }

            if (!_turfIdOrderNumberMap.ContainsKey(turfId))
            {
                _turfIdOrderNumberMap.Add(turfId, _turfIdOrderNumberMap.Count);
            }

            Vercites.Add(new Vector3(TurfMeshData.TurfVercites[0].x, TurfMeshData.TurfVercites[0].y, TurfMeshData.TurfVercites[0].z) + turfPos);
            Vercites.Add(new Vector3(TurfMeshData.TurfVercites[1].x, TurfMeshData.TurfVercites[1].y, TurfMeshData.TurfVercites[1].z) + turfPos);
            Vercites.Add(new Vector3(TurfMeshData.TurfVercites[2].x, TurfMeshData.TurfVercites[2].y, TurfMeshData.TurfVercites[2].z) + turfPos);
            Vercites.Add(new Vector3(TurfMeshData.TurfVercites[3].x, TurfMeshData.TurfVercites[3].y, TurfMeshData.TurfVercites[3].z) + turfPos);
            Uvs.Add(TurfMeshData.TurfTopUvs[0]);
            Uvs.Add(TurfMeshData.TurfTopUvs[1]);
            Uvs.Add(TurfMeshData.TurfTopUvs[2]);
            Uvs.Add(TurfMeshData.TurfTopUvs[3]);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 0);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 1);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 2);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 0);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 2);
            _trianglesTurfIdMap[turfId].Add(_trianglesStartIndex + 3);
            _trianglesStartIndex += 4;
        }

        private void SetMesh()
        {
            Mesh = new Mesh();
            Mesh.vertices = Vercites.ToArray();
            Mesh.uv = Uvs.ToArray();
            Mesh.subMeshCount = Turfs.GetTurfCount();
            for (int i = 0; i < Turfs.GetTurfCount(); i++)
            {
                if (!_trianglesTurfIdMap.ContainsKey(i))
                {
                    Mesh.SetTriangles(new int[0], i);
                    continue;
                }
                Mesh.SetTriangles(_trianglesTurfIdMap[i].ToArray(), i);
            }
            Mesh.RecalculateNormals();
        }

        private void TurfsTypeOnChunk()
        {
            for (int i = 0; i < _chunkTurfIdMap.Length; i++)
            {
                if (_chunkTurfIdMap[i] == 0) continue;
                if (!_trianglesTurfIdMap.ContainsKey(_chunkTurfIdMap[i]))
                {
                    _trianglesTurfIdMap.Add(_chunkTurfIdMap[i], null);
                    _turfSpecies++;
                }
            }
        }

        /// <summary>
        /// �������Ե�ķ���������ȡ��������ı�Ե�������������������sideIndexû��ָ���������飬�򷵻ظ���
        /// </summary>
        /// <param name="index"></param>
        /// <param name="sideIndex"></param>
        /// <returns></returns>
        private int NeighborChunkEdgeIndex(int index, int sideIndex)
        {
            int result = -1;
            Vector2Int pos = M13Mathf.IndexToXY(index);
            Vector2Int dir = new Vector2Int();
            dir.x = sideIndex % 2 == 0 ? 1 : 0;
            dir.y = sideIndex % 2 == 1 ? 1 : 0;
            dir.x = sideIndex == 2 ? dir.x : -dir.x;
            dir.y = sideIndex == 1 ? dir.y : -dir.y;

            Vector2Int norPos = new Vector2Int(-1, -1);
            if (dir.x != 0)
            {
                norPos.x = (pos.x + dir.x) > 15 ? 0 : norPos.x;
                norPos.x = (pos.x + dir.x) < 0 ? 15 : norPos.x;
                norPos.y = pos.y;
            }
            if (dir.y != 0)
            {
                norPos.y = (pos.y + dir.y) > 15 ? 0 : norPos.y;
                norPos.y = (pos.y + dir.y) < 0 ? 15 : norPos.y;
                norPos.x = pos.x;
            }
            result = M13Mathf.XYToIndex(norPos);
            return result;
        }


        private void NeighborTurfIndex(int index)
        {
            Vector2Int pos = new Vector2Int(index >> 4, index % 16);
            Vector2Int pos1 = pos + Vector2Int.left;
            Vector2Int pos2 = pos + Vector2Int.up;
            Vector2Int pos3 = pos + Vector2Int.right;
            Vector2Int pos4 = pos + Vector2Int.down;
            _neighbor[0] = pos1.x < 16 && pos1.x > -1 && pos1.y < 16 && pos1.y > -1 ? pos1.x * 16 + pos1.y : -1;
            _neighbor[1] = pos2.x < 16 && pos2.x > -1 && pos2.y < 16 && pos2.y > -1 ? pos2.x * 16 + pos2.y : -1;
            _neighbor[2] = pos3.x < 16 && pos3.x > -1 && pos3.y < 16 && pos3.y > -1 ? pos3.x * 16 + pos3.y : -1;
            _neighbor[3] = pos4.x < 16 && pos4.x > -1 && pos4.y < 16 && pos4.y > -1 ? pos4.x * 16 + pos4.y : -1;
        }
    }
}